package com.ruoyi.crypto.filter;

import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.crypto.config.SmCryptoConfig;
import com.ruoyi.crypto.utils.SMUtils;
import com.ruoyi.crypto.wrapper.ContentCachingHttpServletRequestWrapper;
import com.ruoyi.crypto.wrapper.ContentCachingHttpServletResponseWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * SM4 加密/解密过滤器，用于处理前后端通信中的加密请求体和响应体。
 */
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class Sm4EncryptionDecryptionFilter extends OncePerRequestFilter {
    @Autowired
    private SmCryptoConfig smCryptoConfig;

    @Autowired
    private SMUtils sm4Base64CryptoUtils;

    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        // 1. 如果未启用加密，直接放行
        if (!smCryptoConfig.isEnabled()) {
            filterChain.doFilter(request, response);
            return;
        }

        // 2. 判断是否是排除的 URL
        if (isExcludedUrl(request)) {
            filterChain.doFilter(request, response);
            return;
        }

        // 3. 判断是否是文件上传请求
        if (isFileUploadRequest(request, smCryptoConfig.getFileUploadContentTypes())) {
            filterChain.doFilter(request, response);
            return;
        }

        // 4. 执行加解密逻辑（因为启用了加密，并且不在排除项中）
        handleEncryptedRequest(request, response, filterChain);
    }

    /**
     * 处理加密请求的核心逻辑
     */
    private void handleEncryptedRequest(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws IOException, ServletException {

        // 读取原始请求体并解密
        byte[] body = readInputStreamFully(request.getInputStream());
        String encryptedRequestBody = new String(body, StandardCharsets.UTF_8);
        String decryptedRequestBody = sm4Base64CryptoUtils.decrypt(encryptedRequestBody);

        // 包装请求对象
        ContentCachingHttpServletRequestWrapper wrappedRequest =
                new ContentCachingHttpServletRequestWrapper(request, decryptedRequestBody.getBytes(StandardCharsets.UTF_8));

        // 包装响应对象
        ContentCachingHttpServletResponseWrapper wrappedResponse =
                new ContentCachingHttpServletResponseWrapper(response);

        // 执行后续过滤器/Controller
        filterChain.doFilter(wrappedRequest, wrappedResponse);

        // 获取响应内容并加密
        byte[] responseBody = wrappedResponse.getContentAsByteArray();
        if (responseBody.length > 0) {
            String original = new String(responseBody, StandardCharsets.UTF_8);
            String encrypted = sm4Base64CryptoUtils.encrypt(original);
            response.setContentLength(encrypted.length());
            response.getWriter().write(encrypted);
        }

        // 将加密后的响应写回客户端
        wrappedResponse.copyBodyToResponse();
    }

    /**
     * 检查当前请求路径是否在 excludeUrls 排除列表中
     */
    private boolean isExcludedUrl(HttpServletRequest request) {
        String requestURI = request.getRequestURI();

        for (String pattern : smCryptoConfig.getExcludeUrls()) {
            if (pathMatcher.match(pattern, requestURI)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否是文件上传请求（根据 Content-Type 判断）
     */
    private boolean isFileUploadRequest(HttpServletRequest request, List<String> fileUploadContentTypes) {
        String contentType = request.getContentType();
        if (StringUtils.isEmpty(contentType)) {
            return false;
        }

        for (String type : fileUploadContentTypes) {
            if (contentType.toLowerCase().startsWith(type.toLowerCase())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 从 InputStream 读取完整字节流
     */
    private byte[] readInputStreamFully(java.io.InputStream inputStream) throws IOException {
        java.io.ByteArrayOutputStream output = new java.io.ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            output.write(buffer, 0, bytesRead);
        }
        return output.toByteArray();
    }
}